// Copyright (c) 2012-2013, yuno. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
package sugarmintcandy.widgetwindow;

import java.util.ArrayList;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.IBinder;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.RemoteViews;
import android.widget.TextView;

public class MainService extends Service {
    private MainApplication mApplication;
    private MainDatabase mDatabase;
    private MyAppWidgetHostManager mHostManager;
    private ArrayList<WidgetWindow> mWindows;
    private SharedPreferences mPreferences;
    private NotificationManager mNotificationManager;
    private NotificationSupport mNotificationSupport;
    private boolean mHasAnyWindows;
    private boolean mShowNotification;
    private int mCurrentWorkspace;
    private boolean mSticky;
    private boolean mStarted;
    private int mStartId;

    @Override
    public void onCreate() {
	Debug.trace();
	super.onCreate();
	mApplication = (MainApplication) getApplication();
	mDatabase = mApplication.getDatabase();
	mHostManager = mApplication.getHostManager();
	mWindows = new ArrayList<WidgetWindow>();
	mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
	mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
	mNotificationSupport = new NotificationSupport();
	fetchPreferences();
	if (mShowNotification) {
	    ApiHelper.startForeground(this, R.id.notification,
		    createNotification());
	    mNotificationSupport.start();
	}
	updateWindows();
    }

    @Override
    public void onDestroy() {
	Debug.trace();
	super.onDestroy();
	mHostManager.stop();
	mNotificationSupport.stop();
	for (WidgetWindow window : mWindows) {
	    window.destroy();
	}
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
	super.onConfigurationChanged(newConfig);
	updateWindows();
    }

    @Override
    public IBinder onBind(Intent intent) {
	return new Binder();
    }

    @Deprecated
    @Override
    public void onStart(Intent intent, int startId) {
	doStartCommand(intent, startId);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
	return doStartCommand(intent, startId);
    }

    private int doStartCommand(Intent intent, int startId) {
	Debug.trace("intent=%s, startId=%d", intent, startId);
	mStarted = true;
	mStartId = startId;
	if (intent != null) {
	    String action = intent.getAction();
	    if (Constants.ACTION_NOTIFICATION_CLICK.equals(action)) {
		mNotificationSupport.onNotificationClick();
	    }
	    if (Constants.ACTION_UPDATE_WINDOWS.equals(action)) {
		updateWindows();
	    }
	    if (Constants.ACTION_SHOW_NOTICE.equals(action)) {
		showNotice();
	    }
	    if (Constants.ACTION_SHOW_NOTIFICATION.equals(action)) {
		if (!mShowNotification) {
		    mShowNotification = true;
		    ApiHelper.startForeground(this, R.id.notification,
			    createNotification());
		    mNotificationSupport.start();
		}
	    }
	    if (Constants.ACTION_HIDE_NOTIFICATION.equals(action)) {
		if (mShowNotification) {
		    mShowNotification = false;
		    ApiHelper.stopForeground(this, R.id.notification, true);
		    mNotificationSupport.stop();
		}
	    }
	    if (Constants.ACTION_SWITCH_WORKSPACE.equals(action)) {
		int workspace = mPreferences.getInt(
			PreferenceKeys.CURRENT_WORKSPACE, 0);
		doSwitchWorkspace(workspace);
	    }
	}
	if (shouldServiceStop()) {
	    mSticky = false;
	    ApiHelper.stopForeground(this, R.id.notification, true);
	    tryStopSelf();
	} else if (canServiceStop()) {
	    mSticky = false;
	} else {
	    mSticky = true;
	}
	if (mSticky) {
	    Debug.trace("returns START_STICKY");
	    return START_STICKY;
	} else {
	    Debug.trace("returns START_NOT_STICKY");
	    return START_NOT_STICKY;
	}
    }

    private void tryStopSelf() {
	if (mStarted) {
	    Debug.trace("stopSelf");
	    stopSelf(mStartId);
	    mStarted = false;
	}
    }

    private boolean shouldServiceStop() {
	if (mHasAnyWindows || mShowNotification) {
	    return false;
	} else {
	    return true;
	}
    }

    private boolean canServiceStop() {
	if (!mWindows.isEmpty() || mShowNotification) {
	    return false;
	} else {
	    return true;
	}
    }

    private void doSwitchWorkspace(int workspace) {
	if (workspace != mCurrentWorkspace) {
	    mCurrentWorkspace = workspace;
	    if (mShowNotification) {
		mNotificationManager.notify(R.id.notification,
			createNotification());
	    }
	    updateWindows();
	}
    }

    private void fetchPreferences() {
	mShowNotification = mPreferences.getBoolean(
		PreferenceKeys.SHOW_NOTIFICATION, false);
	mCurrentWorkspace = mPreferences.getInt(
		PreferenceKeys.CURRENT_WORKSPACE, 0);
    }

    private Notification createNotification() {
	RemoteViews content = new RemoteViews(getPackageName(),
		R.layout.notification);
	int[] buttonIds = { 0, R.id.workspace_button_1,
		R.id.workspace_button_2, R.id.workspace_button_3,
		R.id.workspace_button_4 };
	int[] selectedButtonIds = { 0, R.id.workspace_button_1_selected,
		R.id.workspace_button_2_selected,
		R.id.workspace_button_3_selected,
		R.id.workspace_button_4_selected };
	content.setOnClickPendingIntent(R.id.open_window,
		MainBroadcastReceiver.getOpenWindowPendingIntent(this));
	for (int i = 1; i <= 4; i++) {
	    PendingIntent intent = MainBroadcastReceiver
		    .getSwitchWorkspacePendingIntent(this, i);
	    int id = buttonIds[i];
	    int selectedId = selectedButtonIds[i];
	    if (mCurrentWorkspace == i) {
		content.setViewVisibility(id, View.GONE);
		content.setViewVisibility(selectedId, View.VISIBLE);
		content.setOnClickPendingIntent(selectedId, intent);
	    } else {
		content.setViewVisibility(id, View.VISIBLE);
		content.setViewVisibility(selectedId, View.GONE);
		content.setOnClickPendingIntent(id, intent);
	    }
	}
	int titleColor = getNotificationTitleColor();
	if (titleColor != 0) {
	    content.setTextColor(R.id.title, titleColor);
	}
	Notification notification = new Notification();
	notification.flags |= Notification.FLAG_NO_CLEAR;
	notification.contentView = content;
	notification.contentIntent = createNotificationClickPendingIntent();
	notification.icon = R.drawable.notification;
	notification.when = 0;
	return notification;
    }

    private PendingIntent createNotificationClickPendingIntent() {
	Intent intent = new Intent(this, MainService.class);
	intent.setAction(Constants.ACTION_NOTIFICATION_CLICK);
	return PendingIntent.getService(this, 0, intent, 0);
    }

    @SuppressWarnings("deprecation")
    private int getNotificationTitleColor() {
	String title = getString(R.string.app_name);
	Notification notification = new Notification();
	notification.setLatestEventInfo(this, title, null, null);
	View root = notification.contentView.apply(this, new FrameLayout(this));
	View view = root.findViewById(android.R.id.title);
	if (view instanceof TextView) {
	    return ((TextView) view).getTextColors().getDefaultColor();
	}
	return 0;
    }

    private void showNotice() {
	Log.i(getPackageName(), new String(Utilities
		.decodeBase64("V2lkZ2V0IFdpbmRvdyBDb3B5cmlnaHQg"
			+ "KGMpIDIwMTIsIHl1bm8uIEFsbCByaWdo"
			+ "dHMgcmVzZXJ2ZWQuCg")));
    }

    private void updateWindows() {
	WindowInfo[] infoList = mDatabase.getWindowInfoList();
	mHasAnyWindows = infoList.length > 0;
	int workspaceMask = 1 << mCurrentWorkspace;
	int n = 0;
	for (int i = 0, m = infoList.length; i < m; i++) {
	    WindowInfo info = infoList[i];
	    if ((info.workspace & workspaceMask) == 0) {
		continue;
	    }
	    if (n < mWindows.size()) {
		WidgetWindow window = mWindows.get(n);
		if (window.getAppWidgetId() == info.appWidgetId) {
		    window.updateWindow(info);
		} else {
		    mWindows.remove(n);
		    mHostManager.releaseView(window.getAppWidgetHostView());
		    window.destroy();
		    i--;
		    continue;
		}
	    } else {
		MyAppWidgetHostView view = mHostManager.getView(info);
		WidgetWindow window = new WidgetWindow(this, mDatabase, view,
			info);
		window.show();
		mWindows.add(window);
	    }
	    n++;
	}
	while (mWindows.size() > n) {
	    WidgetWindow window = mWindows.get(n);
	    mWindows.remove(n);
	    mHostManager.releaseView(window.getAppWidgetHostView());
	    window.destroy();
	}
	mHostManager.cleanHosts();
    }

    private class NotificationSupport implements View.OnTouchListener {
	private WindowManager mWindowManager;
	private WindowManager.LayoutParams mParams;
	private float mTouchX;
	private long mEventTime;
	private View mView;
	private boolean mStarted;

	NotificationSupport() {
	    if (ApiHelper.SDK_INT < 11) {
		mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
		mParams = new WindowManager.LayoutParams();
		mParams.width = 0;
		mParams.height = 0;
		mParams.gravity = Gravity.TOP | Gravity.LEFT;
		mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
		mParams.format = PixelFormat.TRANSPARENT;
		mParams.alpha = 0;
		mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
			| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
			| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
			| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
		mView = new View(MainService.this);
		mView.setOnTouchListener(this);
	    }
	}

	void start() {
	    if (mView != null && !mStarted) {
		mWindowManager.addView(mView, mParams);
		mStarted = true;
		Debug.trace();
	    }
	}

	void stop() {
	    if (mStarted) {
		mWindowManager.removeView(mView);
		mStarted = false;
		Debug.trace();
	    }
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
	    if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
		mTouchX = event.getX();
		mEventTime = event.getEventTime();
	    }
	    return true;
	}

	public void onNotificationClick() {
	    if (mStarted) {
		long t = SystemClock.uptimeMillis() - mEventTime;
		if (t >= 0 && t <= 500) {
		    DisplayMetrics dm = getResources().getDisplayMetrics();
		    int buttonSize = Math.round((32 + 4) * dm.density);
		    int reverseIndex = (dm.widthPixels - Math.round(mTouchX))
			    / buttonSize;
		    int index = 4 - reverseIndex;
		    Debug.trace("x=%f, index=%d", mTouchX, index);
		    if (index == 0) {
			Intent intent = new Intent(MainService.this,
				OpenWindowActivity.class);
			intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
				| Intent.FLAG_ACTIVITY_CLEAR_TOP);
			startActivity(intent);
		    }
		    if (index >= 1 && index <= 4) {
			SharedPreferences.Editor editor = mPreferences.edit();
			editor.putInt(PreferenceKeys.CURRENT_WORKSPACE, index);
			editor.commit();
		    }
		}
	    }
	}
    }
}
